package com.gdzn.netty.server.handler;


import com.gdzn.common.core.utils.JsonUtil;
import com.gdzn.common.core.utils.StringUtils;
import com.gdzn.netty.channel.ChannelTcpMap;
import com.gdzn.netty.domain.DeviceMsgDataReq;
import com.gdzn.netty.domain.DeviceMsgDataResp;
import com.gdzn.netty.domain.LoginReq;
import com.gdzn.netty.enums.DeviceMsgEnum;
import com.gdzn.netty.enums.DeviceOnlineEnum;
import com.gdzn.netty.service.IDeviceRedisService;
import com.gdzn.netty.service.IDeviceService;

import io.netty.channel.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Date;

/**
 * 自定义handler,继承简单频道入站处理程序,范围为wen文本套接字Frame
 * websocket间通过frame进行数据的传递和发送
 * 此版本为user与channel绑定的版本，消息会定向发送和接收到指定的user的channel中。
 *
 * @author mac
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class TcpDevicelHandler extends ChannelInboundHandlerAdapter{

    @Autowired
    private IDeviceRedisService deviceRedisService;

    @Autowired
    private IDeviceService deviceService;

    private static TcpDevicelHandler tcpDevicelHandler;

    @PostConstruct
    public void init() {
        tcpDevicelHandler = this;
    }

    /**
     * 当客户端连接服务端之后(打开连接)----->handlerAdded
     * 获取客户端的channel,并且放到ChannelGroup中去管理
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().asLongText();
        ChannelTcpMap.addChannel(channelId,ctx);
        log.info("当前tcp客户端连接数：{}", ChannelTcpMap.channelTcpNum);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        log.info("******channelRead***msg:{}", msg);
        String channelId = ctx.channel().id().asLongText();
        log.info("***netty服务端监控：时间:{},channelId:{}",new Date() , channelId);
        //log.info("***channelId:{},将接收到的数据到msg:{}",channelId, JsonUtil.toJSONString(msg));
        if (!StringUtils.isNull(msg)) {
            readDeviceData(ctx, msg);
        }
    }

    /**
     * 解析协议数据
     * @param ctx
     * @param msg
     */
    private void readDeviceData(ChannelHandlerContext ctx, Object msg) {
        String channelId = ctx.channel().id().asLongText();
        //String resp = JsonUtil.toJSONString(msg);
        //log.info("****readDeviceData***resp:{}",resp);
        DeviceMsgDataReq msgDataReq = JsonUtil.toObject(msg,DeviceMsgDataReq.class);
        //校验设备是否合法
        valid(ctx, msgDataReq);
        //缓存设备ID和通道ID
        tcpDevicelHandler.deviceService.setChannelIdAndDevIdRedis(msgDataReq.getDevId(), channelId);

        log.info("******readDeviceData***msgDataReq:{}",msgDataReq);
        DeviceMsgDataResp respData = new DeviceMsgDataResp();
        try {
            if(null!=msgDataReq&&null!=msgDataReq.getMsgType()){
                if(DeviceMsgEnum.LOGIN.getMsgType()==msgDataReq.getMsgType()){
                    log.info("****readDeviceData***登录***");
                    LoginReq loginReq = JsonUtil.toObject(msg,LoginReq.class);
                    BeanUtils.copyProperties(respData,loginReq);
                    //log.info("****readDeviceData***loginReq:{}",loginReq);
                    respData.setMsgType(111);
                    //处理设备退出登录或连接断了后业务逻辑
                    tcpDevicelHandler.deviceService.updateCabinetOnline(msgDataReq.getDevId(), DeviceOnlineEnum.YES.getCode());
                }else if(DeviceMsgEnum.ATTR_REPORT.getMsgType()==msgDataReq.getMsgType()){
                    log.info("****readDeviceData***属性上报***");
                    BeanUtils.copyProperties(respData,msgDataReq);
                    respData.setMsgType(311);
                }else if(DeviceMsgEnum.ALARM_REPOT.getMsgType()==msgDataReq.getMsgType()){
                    log.info("****readDeviceData***告警上报***");
                    BeanUtils.copyProperties(respData,msgDataReq);
                    respData.setMsgType(411);
                }else if(DeviceMsgEnum.PARAM.getMsgType()==msgDataReq.getMsgType()){
                    log.info("****readDeviceData***远程控制请求***");
                    BeanUtils.copyProperties(respData,msgDataReq);
                    respData.setMsgType(501);
                }else {
                    log.info("******readDeviceData***msgDataReq****没有匹配类型**");
                }
                //保存报文数据
                tcpDevicelHandler.deviceService.sendDataToSave(msgDataReq.getMsgType(), msg);
            }
        }catch (Exception e){
            log.info("******readDeviceData***Exception****Exception**");
        }
        respData.setResult(1);
        //log.info("******readDeviceData***respData:{}",respData);
        sendToClient(ctx, JsonUtil.toJSONString(respData));
        //log.info("******readDeviceData***end******");
    }

    /**
     * 校验数据
     * @param ctx
     * @param msgDataReq
     * @return
     */
    private boolean valid(ChannelHandlerContext ctx, DeviceMsgDataReq msgDataReq) {
        try {
            String authStatus = tcpDevicelHandler.deviceRedisService.getAuthCabinet();
            log.info("***valid***authStatus:{}",authStatus);
            if(StringUtils.isNull(authStatus)||"0".equals(authStatus)){
                return true;
            }
            String devId = msgDataReq.getDevId();
            String existDevId = tcpDevicelHandler.deviceRedisService.getDevId(devId);
            log.info("***valid***devId:{};existDevId:{}",devId,existDevId);
            if(StringUtils.isNull(existDevId)){
                //打印移除的channel
                String channelId = ctx.channel().id().asLongText();
                log.info("***非法设备，不允许连接channelId为：" + channelId+";devId:"+devId);
                ctx.channel().close();
                //关闭通道会触发TcpDevicelHandler.handlerRemoved通道移除方法
                //ChannelTcpMap.removeChannelByName(channelId);
                //tcpDevicelHandler.deviceRedisService.removeChannelIdByDevId(devId);
                return false;
            }
        }catch (Exception e){
            log.info("***valid***exception***");
        }
        return true;
    }

    /**
     * 定义发送数据给客户端的方法
     * @param resultString
     */
    private void sendToClient(ChannelHandlerContext ctx, String resultString) {
        if(StringUtils.isNotEmpty(resultString)){
            try {
                String channelId=ctx.channel().id().asLongText();
                log.info("****sendToClient***channelId:{}",channelId);
                log.info("***sendToClient***服务器发送数据到客户端***resultString:{}",resultString);
                ctx.writeAndFlush(resultString);
                log.info("***sendToClient***服务器发送数据到客户端结束");
//                ctx.channel().writeAndFlush(resultString);
//                ctx.channel().flush();
            } catch (Exception e) {
                log.error("***服务器发送数据给客户端失败,{},关闭：{}",e);
                log.info("***服务器发送数据给客户端失败,关闭服务");
//                ctx.channel().close();
            }
        }
    }


    /**
     * 处理器移除时,移除channelGroup中的channel
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //打印移除的channel
        String channelId = ctx.channel().id().asLongText();
        String devId = tcpDevicelHandler.deviceRedisService.getDevIdByChannelId(channelId);
        log.info("客户端被移除，channelId:{};devId:{}", channelId,devId);
        ctx.channel().close();
        ChannelTcpMap.removeChannelByName(channelId);
        tcpDevicelHandler.deviceService.removeChannelIdAndDevIdRedis(devId, channelId);

        //处理设备退出登录或连接断了后业务逻辑
        tcpDevicelHandler.deviceService.updateCabinetOnline(devId, DeviceOnlineEnum.NO.getCode());
    }
}